#pragma once
#include "../infra/elasticsearch.hpp"
#include "user.hxx"
#include <bits/stdint-uintn.h>
#include <boost/date_time/posix_time/conversion.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <json/value.h>
#include <memory>
#include <string>
#include <vector>

namespace shared {

namespace dal {

using ESClientPtr = std::shared_ptr<elasticlient::Client>;

class ESClientFactory 
{
public:
    static ESClientPtr create(const std::vector<std::string>& hostList)
    {
        return std::make_shared<elasticlient::Client>(hostList);
    }
};

class UserSearch
{
public:
    UserSearch(const ESClientPtr& client) : esClient_(client) {}

    bool createIndex()
    {
        if(shared::infra::ESIndex(esClient_, "user")
            .append(UserFields::ID, "keyword", "standard", true)
            .append(UserFields::NICKNAME)
            .append(UserFields::PHONE, "keyword", "standard", true)
            .append(UserFields::DESCRIPTION, "text", "standard", false)
            .append(UserFields::AVATAR_ID, "keyword", "standard", false)
            .create() == false) 
        {
            LOG_INFO("用户信息索引创建失败.");
            return false;
        }
        return true;
    }

    bool appendData(const std::string& userId, 
        const std::string& nickname, 
        const std::string& phone, 
        const std::string& description, const std::string& avatarId)
    {
        
        if(infra::ESInsert(esClient_, "user")
            .append(UserFields::ID, userId)
            .append(UserFields::NICKNAME, nickname)
            .append(UserFields::PHONE, phone)
            .append(UserFields::DESCRIPTION, description)
            .append(UserFields::AVATAR_ID, avatarId)
            .insert(userId) == false)
        {
            LOG_ERROR("用户数据插入或更新失败.");
            return false;
        }
        return true;
    }

    std::vector<entity::User> search(const std::string& key, const std::vector<std::string>& userIds)
    {
        Json::Value userJson = shared::infra::ESSearch(esClient_, "user")
            .append_should_match(UserFields::ID_KEYWORD, key)
            .append_should_match(UserFields::PHONE_KEYWORD, key)
            .append_should_match(UserFields::NICKNAME, key)
            .append_must_not_terms(UserFields::ID_KEYWORD, userIds)
            .search();

        if (userJson.isArray() == false) {
            LOG_ERROR("用户搜索结果为空，或者结果不是数组类型");
            return std::vector<entity::User>();
        }

        std::vector<entity::User> result;
        for(auto& obj : userJson) {
            entity::User user;
            user.setUserId(obj["_source"][UserFields::ID].asString());
            user.setNickname(obj["_source"][UserFields::NICKNAME].asString());
            user.setPhone(obj["_source"][UserFields::PHONE].asString());
            user.setDscription(obj["_source"][UserFields::DESCRIPTION].asString());
            user.setAvatarId(obj["_source"][UserFields::AVATAR_ID].asString());
            result.emplace_back(user);
        }
        return result;
    }

private:
    ESClientPtr esClient_;
};


class MessageSearch
{
public:
    MessageSearch(const ESClientPtr& client) : esClient_(client) {}

    bool createIndex()
    {
        if(shared::infra::ESIndex(esClient_, "user")
            .append(MessageFields::SESSION_ID, "keyword", "standard", true)
            .append(MessageFields::MESSAGE_ID, "keyword", "standard", false)
            .append(MessageFields::USER_ID, "keyword", "standard", false)
            .append(MessageFields::CREATE_TIME, "long", "standard", false)
            .append(MessageFields::CONTENT)
            .create() == false) 
        {
            LOG_INFO("消息信息索引创建失败.");
            return false;
        }
        return true;
    }
    bool appendData(const std::string& sessionId, 
        const std::string& messageId, 
        const std::string& userId, 
        const uint64_t createTime, 
        const std::string& content)
    {
        
        if(infra::ESInsert(esClient_, "message")
            .append(MessageFields::SESSION_ID, sessionId)
            .append(MessageFields::MESSAGE_ID, messageId)
            .append(MessageFields::USER_ID, userId)
            .append(MessageFields::CREATE_TIME, createTime)
            .append(MessageFields::CONTENT, content)
            .insert(messageId) == false)
        {
            LOG_ERROR("消息数据插入或更新失败.");
            return false;
        }
        return true;
    }
    bool remove(const std::string& sessionId)
    {   
        if(infra::ESRemove(esClient_, "message").remove(sessionId) == false) {
            LOG_ERROR("消息数据删除失败!");
            return false;
        }
        return true;
    }
    std::vector<entity::Message> search(const std::string& key, const std::string& sessionId)
    {
        Json::Value messageJson = shared::infra::ESSearch(esClient_, "message")
            .append_must_terms(MessageFields::SESSION_ID_KEYWORD, sessionId)
            .append_must_match(MessageFields::CONTENT, key)
            .search();

        std::vector<entity::Message> result;
        if (messageJson.isArray() == false) {
            LOG_ERROR("消息搜索结果为空，或者结果不是数组类型");
            return result;
        }
        for(auto& obj : messageJson) {
            entity::Message msg;
            msg.setSessionId(obj["_source"][MessageFields::SESSION_ID].asString());
            msg.setMessageId(obj["_source"][MessageFields::MESSAGE_ID].asString());
            msg.setUserId(obj["_source"][MessageFields::USER_ID].asString());
            msg.setCreateTime(boost::posix_time::from_time_t(obj["_source"][MessageFields::CREATE_TIME].asUInt64()));
            msg.setContent(obj["_source"][MessageFields::CONTENT].asString());
            result.emplace_back(msg);
        }
        return result;
    }

private:
    ESClientPtr esClient_;
};

using UserSearchPtr = std::shared_ptr<UserSearch>;
using MessageSearchPtr = std::shared_ptr<MessageSearch>;

}}